AWS Backup からプライベート IP を固定して EC2 を復元する際、jq を使ってお手軽に実行できるようにしてみた
こんにちは。枡川です。
AWS Backup で EC2 インスタンスを復元する際、システム要件で既存のプライベート IP を引き継ぐ必要がある場合もあると思います。
その際、AWS Backup のサービスページからの復元ではプライベート IP を指定することができません。
AMI ID を直接指定して EC2 のサービスページから復元を行うことは可能ですが、新しく EC2 を新規構築する手順と変わらないため、どうしても設定項目が多くなってしまいます。
そこで、AWS CLI を利用して、プライベート IP を指定しつつ AWS Backup 経由でリストアする方法を試してみました。
また、リストアのために JSON ファイルの準備が必要になるのですが、思ったより手間だったので jq を利用して手順を簡略化してみました。
AWS Backup でのプライベート IP を指定したリストアについて
AWS Backup の start-restore-job コマンドを利用してリストアを行います。
この際、--metadata
オプションとして JSON ファイルを指定することができ、この中でプライベート IP などの指定を行うことができます。
また、バックアップ取得元インスタンスの情報は get-recovery-point-restore-metadata コマンドで取得できるため、こちらから取得した情報を元に JSON ファイルを生成することが可能です。
プライベート IP を指定して AWS Backup 経由でリストアする理屈はシンプルですが、get-recovery-point-restore-metadata コマンドで取得した情報をそのまま start-restore-job で利用することはできず、それなりに変換が必要です。
get-recovery-point-restore-metadata で情報を取得した後、変換が必要な項目について
変換が必要な内容を確認します。
RestoreMetadata 属性を抜き出す必要がある
get-recovery-point-restore-metadata のレスポンスは下記のようになりますが、RestoreMetadata だけを抜き出す必要があります。
{
"BackupVaultArn": "arn:aws:backup:ap-northeast-1:xxxxxxxxxxxxx:backup-vault:Default",
"RecoveryPointArn": "arn:aws:ec2:ap-northeast-1::image/ami-070805fd4be75c6f3",
"RestoreMetadata": {
"Architecture": "x86_64",
"CapacityReservationSpecification": "{\"CapacityReservationPreference\":\"open\"}",
"CpuOptions": "{\"CoreCount\":1,\"ThreadsPerCore\":2}",
"CreditSpecification": "{\"CpuCredits\":\"unlimited\"}",
"DisableApiTermination": "false",
"EbsOptimized": "false",
"EnaSupport": "true",
"HibernationOptions": "{\"Configured\":false}",
"IamInstanceProfileName": "EcsTransferFamilyStack-BastionBastionbastionhostInstanceProfileA17C8709-GQ3vua4aFyID",
"InstanceInitiatedShutdownBehavior": "stop",
"InstanceType": "t3.micro",
"Monitoring": "{\"State\":\"disabled\"}",
"NetworkInterfaces": "[{\"AssociatePublicIpAddress\":true,\"DeleteOnTermination\":true,\"Description\":\"\",\"DeviceIndex\":0,\"Groups\":[\"sg-09c735afff02550a4\"],\"Ipv6AddressCount\":0,\"Ipv6Addresses\":[],\"NetworkInterfaceId\":\"eni-0e71d0cd7acf0b4f1\",\"PrivateIpAddress\":\"10.100.5.253\",\"PrivateIpAddresses\":[{\"Primary\":true,\"PrivateIpAddress\":\"10.100.5.253\"}],\"SecondaryPrivateIpAddressCount\":0,\"SubnetId\":\"subnet-0b40ad6d419ddd266\",\"InterfaceType\":\"interface\",\"Ipv4Prefixes\":[],\"Ipv6Prefixes\":[]}]",
"Placement": "{\"AvailabilityZone\":\"ap-northeast-1a\",\"GroupName\":\"\",\"Tenancy\":\"default\"}",
"RequireIMDSv2": "true",
"RootDeviceType": "ebs",
"SecurityGroupIds": "[\"sg-09c735afff02550a4\"]",
"SubnetId": "subnet-0b40ad6d419ddd266",
"VirtualizationType": "hvm",
"VpcId": "vpc-05b9ef1203d324989",
"aws:backup:request-id": "a25f0826-9a59-40d4-aab2-b50575c03517"
},
"ResourceType": "EC2"
}
SecurityGroupIds と SubnetId を削除
RestoreMetadata だけを抜き出すと下記のようになります。
{
"Architecture": "x86_64",
"CapacityReservationSpecification": "{\"CapacityReservationPreference\":\"open\"}",
"CpuOptions": "{\"CoreCount\":1,\"ThreadsPerCore\":2}",
"CreditSpecification": "{\"CpuCredits\":\"unlimited\"}",
"DisableApiTermination": "false",
"EbsOptimized": "false",
"EnaSupport": "true",
"HibernationOptions": "{\"Configured\":false}",
"IamInstanceProfileName": "EcsTransferFamilyStack-BastionBastionbastionhostInstanceProfileA17C8709-GQ3vua4aFyID",
"InstanceInitiatedShutdownBehavior": "stop",
"InstanceType": "t3.micro",
"Monitoring": "{\"State\":\"disabled\"}",
"NetworkInterfaces": "[{\"AssociatePublicIpAddress\":true,\"DeleteOnTermination\":true,\"Description\":\"\",\"DeviceIndex\":0,\"Groups\":[\"sg-09c735afff02550a4\"],\"Ipv6AddressCount\":0,\"Ipv6Addresses\":[],\"NetworkInterfaceId\":\"eni-0e71d0cd7acf0b4f1\",\"PrivateIpAddress\":\"10.100.5.253\",\"PrivateIpAddresses\":[{\"Primary\":true,\"PrivateIpAddress\":\"10.100.5.253\"}],\"SecondaryPrivateIpAddressCount\":0,\"SubnetId\":\"subnet-0b40ad6d419ddd266\",\"InterfaceType\":\"interface\",\"Ipv4Prefixes\":[],\"Ipv6Prefixes\":[]}]",
"Placement": "{\"AvailabilityZone\":\"ap-northeast-1a\",\"GroupName\":\"\",\"Tenancy\":\"default\"}",
"RequireIMDSv2": "true",
"RootDeviceType": "ebs",
"SecurityGroupIds": "[\"sg-09c735afff02550a4\"]",
"SubnetId": "subnet-0b40ad6d419ddd266",
"VirtualizationType": "hvm",
"VpcId": "vpc-05b9ef1203d324989",
"aws:backup:request-id": "a25f0826-9a59-40d4-aab2-b50575c03517"
}
この内、SecurityGroupIds と SubnetId は NetworkInterfaces 内に含まれる情報と重複しています。
そのため、属性を削除しないとリストア時に下記のようなエラーが発生します。
SecurityGroupIds が重複している場合
Network interfaces and an instance-level security groups may not be specified on the same request
SubnetId が重複している場合
Network interfaces and an instance-level subnet ID may not be specified on the same request
NetworkInterfaces 属性にある PrivateIpAddress と SecondaryPrivateIpAddressCount を削除
get-recovery-point-restore-metadata のレスポンスでは NetworkInterfaces 属性内に PrivateIpAddress 属性と PrivateIpAddresses 属性が両方存在しています。
"NetworkInterfaces": "[{\"AssociatePublicIpAddress\":true,\"DeleteOnTermination\":true,\"Description\":\"\",\"DeviceIndex\":0,\"Groups\":[\"sg-09c735afff02550a4\"],\"Ipv6AddressCount\":0,\"Ipv6Addresses\":[],\"NetworkInterfaceId\":\"eni-0e71d0cd7acf0b4f1\",\"PrivateIpAddress\":\"10.100.5.253\",\"PrivateIpAddresses\":[{\"Primary\":true,\"PrivateIpAddress\":\"10.100.5.253\"}],\"SecondaryPrivateIpAddressCount\":0,\"SubnetId\":\"subnet-0b40ad6d419ddd266\",\"InterfaceType\":\"interface\",\"Ipv4Prefixes\":[],\"Ipv6Prefixes\":[]}]",
このまま利用すると下記エラーが発生するので、今回は PrivateIpAddress 属性を削除します。
Only one primary private IP address can be specified.
また、secondaryPrivateIpAddressCount 属性が 0 だと下記エラーになるので、今回は ENI が一つであると想定して削除します。
Value (0) for parameter secondaryPrivateIpAddressCount is invalid. Value must be a positive number.
セカンダリ IP がある場合は削除不要なので、ご注意下さい。
NetworkInterfaces 属性にある AssociatePublicIpAddress を false にする
get-recovery-point-restore-metadata から取得すると、EC2 の状態に関わらず AssociatePublicIpAddress が true になってしまうようです。
この状態だとリストアした EC2 に対してパブリック IP が紐づいてしまうので、false に変更します。
jq を使ってリストア用の JSON ファイルを生成してみる
これまでの手順を踏まえて、jq を利用したコマンドを作成してみました。
aws backup get-recovery-point-restore-metadata \
--backup-vault-name ${BACKUP_VAULT_NAME} \
--recovery-point-arn ${RECOVERY_POINT_ARN} \
| jq '.RestoreMetadata | del(.SubnetId, .SecurityGroupIds) | .NetworkInterfaces |= (fromjson | .[0] |= del(.PrivateIpAddress, .NetworkInterfaceId, .SecondaryPrivateIpAddressCount) | .[0].AssociatePublicIpAddress = false | tojson)' > meta_data.json
生成したメタデータを利用してリストアを実行してみます。
% aws backup start-restore-job \
--recovery-point-arn ${RECOVERY_POINT_ARN} \
--iam-role-arn ${BACKUP_ROLE_ARN} \
--metadata file://meta_data.json \
--copy-source-tags-to-restored-resource
{
"RestoreJobId": "E929C76E-7CCF-979C-55BA-A8F803C4F2A9"
}
無事リストアも成功しました。
% aws backup describe-restore-job --restore-job-id E929C76E-7CCF-979C-55BA-A8F803C4F2A9
{
"AccountId": "xxxxxxxxxxxx",
"RestoreJobId": "E929C76E-7CCF-979C-55BA-A8F803C4F2A9",
"RecoveryPointArn": "arn:aws:ec2:ap-northeast-1::image/ami-070805fd4be75c6f3",
"CreationDate": "2025-01-03T01:14:18.361000+09:00",
"CompletionDate": "2025-01-03T01:15:35.172000+09:00",
"Status": "COMPLETED",
"PercentDone": "0.00%",
"BackupSizeInBytes": 8589934592,
"IamRoleArn": "arn:aws:iam::xxxxxxxxxxxx:role/AWSBackupRoleWithPassRole",
"CreatedResourceArn": "arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:instance/i-06f933962cd38d33a",
"ResourceType": "EC2",
"RecoveryPointCreationDate": "2025-01-02T20:30:00+09:00",
"CreatedBy": {}
}
まとめ
AWS Backup からプライベート IP を固定して EC2 を復元してみました。
バックアップ元の EC2 で利用していたプライベート IP を使う場合は事前に IP の開放(インスタンスの削除)が必要なのでご注意下さい。
それなりに手間なので、料金が許容できるのであれば NLB で IP 固定できると良いなと思いました。
本ブログがどなたかの参考になれば幸いです。